package cn.patient.pp.module.patients.service;

import cn.hutool.core.util.StrUtil;
import cn.patient.pp.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.patient.pp.module.patients.controller.admin.vo.importExecl.ImportRespVO;
import cn.patient.pp.module.patients.controller.admin.vo.importExecl.PatientsDOExeclVO;
import cn.patient.pp.module.patients.controller.admin.vo.PatientsDOPageReqVO;
import cn.patient.pp.module.patients.controller.admin.vo.PatientsDOSaveReqVO;
import cn.patient.pp.module.patients.framework.web.config.Config;
import cn.patient.pp.module.patients.framework.web.config.LogRecordConstants;
import cn.patient.pp.module.patients.framework.web.config.async.AsyncTaskServie;
import cn.patient.pp.module.report.dal.dataobject.report.PatientInfoDO;
import cn.patient.pp.module.report.dal.mysql.report.ReportMapper;
import cn.patient.pp.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.patient.pp.module.system.controller.admin.user.vo.user.UserImportRespVO;
import cn.patient.pp.module.system.dal.dataobject.dept.DeptDO;
import cn.patient.pp.module.system.dal.dataobject.permission.RoleDO;
import cn.patient.pp.module.system.dal.dataobject.user.AdminUserDO;
import cn.patient.pp.module.system.service.dept.DeptService;
import cn.patient.pp.module.system.service.permission.PermissionService;
import cn.patient.pp.module.system.service.user.AdminUserService;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import cn.patient.pp.module.patients.dal.dataobject.patients.PatientsDODO;
import cn.patient.pp.framework.common.pojo.PageResult;
import cn.patient.pp.framework.common.util.object.BeanUtils;

import cn.patient.pp.module.patients.dal.mysql.patients.PatientsDOMapper;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static cn.patient.pp.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.patient.pp.module.infra.enums.ErrorCodeConstants.DO_NOT_EXISTS;
import static cn.patient.pp.module.patients.framework.web.config.LogRecordConstants.FOLLOW_UP_DEATIL;
import static cn.patient.pp.module.system.dal.redis.RedisKeyConstants.*;

/**
 * 患者总 Service 实现类
 *
 * @author sailv
 */
@Service
@Validated
public class PatientsDOServiceImpl implements PatientsDOService {

    @Resource
    private PatientsDOMapper dOMapper;

    @Resource
    private DeptService deptService;

    @Resource
    private AsyncTaskServie asyncTaskServie;
    @Resource
    private AdminUserService adminUserService;

    @Resource
    private PermissionService permissionService;

    @Resource
    private ReportMapper reportMapper;


    @Override
    public Integer createDO(PatientsDOSaveReqVO createReqVO) {
        // 插入
        PatientsDODO dO = BeanUtils.toBean(createReqVO, PatientsDODO.class);
        dOMapper.insert(dO);
        // 返回
        return dO.getId();
    }

    @Override
    public void updateDO(PatientsDOSaveReqVO updateReqVO) {
        // 校验存在
        validateDOExists(updateReqVO.getId());
        // 更新
        PatientsDODO updateObj = BeanUtils.toBean(updateReqVO, PatientsDODO.class);
        dOMapper.updateById(updateObj);
    }

    @Override
    public void deleteDO(Integer id) {
        // 校验存在
        String idNumber = validateDOExists(id);
        // 删除
        dOMapper.delete("id_number",idNumber);
    }

    @Override
    public Integer deleteDOByCounty(PatientsDODO reqVO) {
        // 删除
        return  dOMapper.deleteByIdsAndPatientCode(new HashSet<String>(Collections.singleton(reqVO.getIdNumber())),reqVO.getPatientCode());
    }

    private String validateDOExists(Integer id) {
        PatientsDODO patientsDODO = dOMapper.selectById(id);
        if (patientsDODO == null) {
            throw exception(DO_NOT_EXISTS);
        }
        return patientsDODO.getIdNumber();
    }

    @Override
    @LogRecord(success = LogRecordConstants.PATIENT_DEATIL_SUCCESS,
            operator = "{{#currentUser}}",
            type = LogRecordConstants.PATIENT,
            fail = "查看详情失败，失败原因：「{{#_errorMsg}}」",
            subType = LogRecordConstants.PATIENT_DEATIL,bizNo = "{{#id}}")
    public PatientsDODO getDO(Integer id) {
        return dOMapper.selectById(id);
    }

    @Override
//    @Cacheable(
//            cacheNames = PATIENTS_PAGE,
//            key        = "'allPatientsPage_'+#userId+'_'+#pageReqVO.pageNo+'_'+#pageReqVO.pageSize",
//            condition  = "#pageReqVO.deptIds.empty"
//                    + " and #pageReqVO.patientName==null"
//                    + " and #pageReqVO.patientCode==null"
//                    + " and #pageReqVO.idNumber==null"
//                    + " and #pageReqVO.nextFollowupDate.length==0"
//                    + " and #pageReqVO.gender==null"
//                    + " and #pageReqVO.currentAddress==null"
//                    + " and #pageReqVO.lastFollowupDate.length==0"
//                    + " and #pageReqVO.hospitalizationStatus==null"
//                    + " and #pageReqVO.riskAssessment==null"
//                    + " and #pageReqVO.dangerousBehavior==null"
//    )
    @LogRecord(success = LogRecordConstants. PATIENT_PAGE_SUCCESS,
            operator = "{{#currentUser}}",
            type = LogRecordConstants.PATIENT,
            fail = "查看分页失败，失败原因：「{{#_errorMsg}}」",
            subType = LogRecordConstants.PATIENT_PAGE,bizNo = LogRecordConstants.PATIENT_PAGE_BIZ_NO)
    public PageResult<PatientsDODO> getDOPage(PatientsDOPageReqVO pageReqVO, Long userId) {
        if (pageReqVO.getDeptIds().isEmpty()){
            AdminUserDO user = adminUserService.getUser(userId);
            List<Long> deptIds = deptService.getDeptIdsAndOwnId(user.getDeptId());
            pageReqVO.setDeptIds(deptIds);
        }
        //查询最新的人员id，但是现在基本信息只保留人最新的数据，所以去掉这行代码
//        List<Integer> ids = dOMapper.selectLatestIdsNew(pageReqVO, deptIds);
//        List<RoleDO> roleDOS = permissionService.getEnableUserRoleListByUserIdFromCache(userId);
//        boolean hasPoliceRole = roleDOS.stream()
//                .anyMatch(role -> "police".equals(role.getCode()));
//        return dOMapper.selectPage(pageReqVO, ids, hasPoliceRole);
        return dOMapper.selectPage(pageReqVO, new ArrayList<>());
    }

    @Override
    @CacheEvict(value = {PATIENTS_PAGE,RISK_PROPORTION,RISK_TYPE_PROPORTION,INCREASE_PROPORTION,HIGH_RISK_PATIENTS,FOLLOW_IP_TRENDS}, allEntries = true)
    @LogRecord(success = LogRecordConstants.PATIENT_IMPORT_SUCCESS,
            operator = "{{#currentUser}}",
            type = LogRecordConstants.PATIENT,
            fail = "导入基本信息失败，失败原因：「{{#_errorMsg}}」",
            subType = LogRecordConstants.PATIENT_IMPORT,bizNo = LogRecordConstants.PATIENT_IMPORT_BIZ_NO)
    public ImportRespVO  importBaseInfoList(List<PatientsDOExeclVO> list, Boolean updateSupport) throws InterruptedException {

        // 2. 遍历，逐个创建 or 更新
        ImportRespVO respVO = ImportRespVO.builder().createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).total(new AtomicInteger(0)).build();

        List<DeptDO> deptList = deptService.getDeptList(new DeptListReqVO());
        // 保留第一个出现的 DeptDO
        Map<String, Long> deptMap = deptList.stream().collect(
                Collectors.toMap(
                        DeptDO::getName, DeptDO::getId,
                        (existing, replacement) -> existing));

        for (PatientsDOExeclVO patientsDOExeclVO : list) {
            //赋值部门id
            if (StrUtil.isNotBlank(patientsDOExeclVO.getManagementOrgName())) {
                patientsDOExeclVO.setDeptId(deptMap.get(patientsDOExeclVO.getManagementOrgName()));
            }
//            //赋值疾病分类，都没命中字典，则是null,默认是其他
//            if (patientsDOExeclVO.getCurrentDiagnosis() == null) {
//                patientsDOExeclVO.setCurrentDiagnosis("99");
//            }
        }

        //切分数据
        List<List<PatientsDOExeclVO>> batchList = splitList(list, Config.BATHCH_SIZE);
        //定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                Config.CORE_THREAD_NUM, Config.MAX_THREAD_NUM, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        //将任务提交给线程池执行，同时提供一个队列保存执行结果，你可以“谁先完成谁先处理”。
        CompletionService<Boolean> completionService = new ExecutorCompletionService<>(executor);
        //多线程分批发入数据库
        long start = System.currentTimeMillis(); // 记录开始时间
        for (List<PatientsDOExeclVO> batch : batchList) {
            completionService.submit(() -> {
                dOMapper.insertIgnoreBatch(batch);
                respVO.getTotal().addAndGet(batch.size());
                return true;
            });
        }
        //检查异常
        for (int i = 0; i < batchList.size(); i++) {
            Future<Boolean> result = completionService.take(); // 按完成顺序取出
            long end = System.currentTimeMillis();   // 记录结束时间
            System.out.println("线程[" + Thread.currentThread().getName() + "] 处理耗时：" + (end - start) + " ms");
            try {
                result.get(); // 捕获异常
            } catch (Exception e) {
                e.printStackTrace(); // 可换成记录日志
            }
        }
        executor.shutdown();

        //调用后置查询省厅是否已上传数据接口
        asyncTaskServie.deletePatientWithZero();
        return respVO;
    }

    //多线程切割execl数据
    public static <T> List<List<T>> splitList(List<T> list, int batchSize) {
        List<List<T>> result = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            result.add(list.subList(i, Math.min(i + batchSize, list.size())));
        }
        return result;
    }

    @Override
    public PatientsDODO getByPatientCode(String patientCode) {
        LambdaQueryWrapperX<PatientsDODO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(PatientsDODO::getPatientCode, patientCode);
        return dOMapper.selectOne(queryWrapper);
    }

    @Override
    public PatientsDODO getByReportPatientInfoId(Long id) {
        PatientInfoDO patientInfoDO = reportMapper.selectById(id);
        if (!Objects.isNull(patientInfoDO)) {
            LambdaQueryWrapperX<PatientsDODO> queryWrapper = new LambdaQueryWrapperX<>();
            queryWrapper.eqIfPresent(PatientsDODO::getIdNumber, patientInfoDO.getIdNumber());
            return dOMapper.selectOne(queryWrapper);
        }
        return null;
    }
}
